// namespace
if (window.demo === undefined) {
    var demo = {};
}

demo.utils = {};

/**
 *
 * @param {Object} value
 */
demo.utils.isNumber = function(value) {
    return (typeof value === "number") && isFinite(value);
};

/**
 *
 * @param {Object} value
 */
demo.utils.toInt = function(value) {
    return Math.floor(value);
};

/**
 * Format a number to readable string.
 * @param {Number} value
 */
demo.utils.formatNumber = function(value) {
    // e.g. 123456789 -> [123, 456, 789] -> 123,456,789.123
    var parts = [];
    while (value > 0) {
        var num = value % 1000; // 78
        var part = (value < 1000 || num > 100) ? (num + "") : this.pad(num + "", "0", 3); // "078"
        parts.unshift(part); // [789] -> [456, 789] -> [123, 456, 789]
        value = Math.floor(value / 1000);
    }

    return parts.join(",");
};


/**
 * Generate a random integer in a range.
 * 
 * @param {Integer} start
 * @param {Integer} end
 */
demo.utils.randomInt = function(start, end) {
    var gap = end - start + 1;
    return Math.floor(Math.random() * gap + start);
};

/**
 * Insert item to an array.
 * @param {Object} item The item.
 * @param {Object} array The array.
 * @param {Object} insertToFirst true if item will be inserted as first item.
 */
demo.utils.insertToArray = function(item, array, insertToFirst) {
    if (insertToFirst) {
        array.unshift(item);
    } else {
        array.push(item);
    }
};

demo.utils.getMappedValue = function(value, mapping)
{
  var key = value + "";
  var mappedValue = mapping[key];
  return (mappedValue === undefined) ? value : mappedValue;
};

/**
 * Left pad a string
 * @param {String} str
 * @param {String} padChar
 * @param {Integer} length
 */
demo.utils.pad = function(str, padChar, length) {
    var out = str + "";
    var padNum = length - out.length;
    var pad = (padNum > 0) ? new Array(padNum + 1).join(padChar) : "";

    return pad + out;
};

/**
 * Replace the template with the specified map and pattern.
 * Usage: {@code replace('{0}/{1}/{2}', ['a', 'b', 'c'])} -> {@code 'a/b/c')}.
 * @see dojo.replace
 *
 * @param {String} tmpl The template string.
 * @param {(Array|Object)} map The map used to provide the arguments. It would be an array for {0} and an object for {name}.
 * @param {String?} pattern The pattern used to parse map.
 * @return {String} The replaced string.
 */
demo.utils.replace = function(tmpl, map, pattern) {
    var _pattern = /\{([^\}]+)\}/g;

    var output = tmpl.replace(pattern || _pattern, function(str, key) {
        return map[key] === undefined ? str : map[key];
    });

    return output;
};

/**
 * Format the receiver using the specified pattern and args.
 * Usage: {@code format('{0}/{1}/{2}', ['a', 'b', 'c'])} -> {@code 'a/b/c')}.
 * @param {String} pattern The pattern to use.
 * @param {...*} args The format arguments to use.
 * @return The formatted string.
 */
demo.utils.format = function(pattern, args) {
    // filter out the format arguments
    var args = dojo.filter(arguments, function(argument, index) {
        return index > 0;
    });

    var masks = pattern.match(/{\d+}/ig);
    var formatted = pattern;

    dojo.forEach(masks, function(mask) {
        var paramIndex = -1;
        var result = mask.match(/{(\d+)}/);
        if (result.length > 1) {
            paramIndex = result[1];
        }

        var param = args[paramIndex];
        if (param !== undefined) {
            // In JavaScript, string.replace(substr, replacement) will apply special replacement patterns.
            // @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
            // e.g., "$$" means insert a "$".
            // So "ab".replace("b", "$$b") will be "a$b" rather than "a$$b".
            // To avoid that, here we will use a function to always return the actual replacement string.
            formatted = formatted.replace(mask, function(str) {
                return param;
            });
        }
    });

    return formatted;
};

/**
 * Extract the string using the specified pattern.
 * Usage: <code>extract("a-b-c", "{0}-{1}-{2}")</code> -> <code>["a", "b", "c"]</code>
 * @param {String} pattern the pattern to use
 * @param {Object...} args the format arguments to use
 * @return the formatted string
 */
demo.utils.extract = function(str, pattern) {
    // filter out the format arguments
    var args = dojo.filter(arguments, function(argument, index) {
        return index > 0;
    });

    var masks = pattern.match(/{\d+}/ig);
    var formatted = pattern;

    dojo.forEach(masks, function(mask) {
        var paramIndex = -1;
        var result = mask.match(/{(\d+)}/);
        if (result.length > 1) {
            paramIndex = result[1];
        }

        var param = args[paramIndex];
        if (param !== undefined) {
            // In JavaScript, string.replace(substr, replacement) will apply special replacement patterns.
            // @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
            // e.g., "$$" means insert a "$".
            // So "ab".replace("b", "$$b") will be "a$b" rather than "a$$b".
            // To avoid that, here we will use a function to always return the actual replacement string.
            formatted = formatted.replace(mask, function(str) {
                return param;
            });
        }
    });

    return formatted;
};

/**
 * Add highlight effect to a string.
 * @param {Object} value
 * @param {Object} keyword
 */
demo.utils.highlight = function(value, keyword) {
    if (!value) {
        return value;
    }

    if (!keyword) {
        return value;
    }

    var highlightPattern = '<span class="highlight">{0}</span>';
    var pattern = keyword;
    var regExp = new RegExp(pattern, "ig");
    var highlighted = value.replace(regExp, function(str) {
        return demo.utils.format(highlightPattern, str);
    });

    return highlighted;
};


//-----------------------------------------------------
/**
 * Formats the time gap as a string.
 * 
 * The format used is ISO8601-like: "H:m:s.S".
 * 
 * @param {Integer} durationMillis - the duration to format
 * @param {Boolean} includeMillis - true to show milliseconds
 * @return {String} the formatted duration, not null
 */
demo.utils.formatDurationHMS = function(durationMillis, includeMillis) {
    var utils = demo.utils;

    var MILLIS_PER_SECOND = 1000;
    var MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
    var MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
    var MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

    var days = Math.floor(durationMillis / MILLIS_PER_DAY);
    durationMillis = durationMillis - days * MILLIS_PER_DAY;

    var hours = Math.floor(durationMillis / MILLIS_PER_HOUR);
    durationMillis = durationMillis - hours * MILLIS_PER_HOUR;

    var minutes = Math.floor(durationMillis / MILLIS_PER_MINUTE);
    durationMillis = durationMillis - minutes * MILLIS_PER_MINUTE;

    var seconds = Math.floor(durationMillis / MILLIS_PER_SECOND);
    durationMillis = durationMillis - seconds * MILLIS_PER_SECOND;

    var milliseconds = Math.floor(durationMillis);

    var padZero = function(number) {
        return utils.pad(number, "0", 2);
    };

    var str = utils.replace("{0}:{1}:{2}", [padZero(hours), padZero(minutes), padZero(seconds)]);
    if (includeMillis) {
        str += "." + padZero(milliseconds);
    }

    return str;
};

dojo.require("dojox.collections.SortedList");
demo.utils.diagnoseUrl = function(url) {
    var regExp = /[?&](\w+?)=([^&]+)/ig;
    var result = new dojox.collections.SortedList();

    var matches = null;
    while ((matches = regExp.exec(url)) !== null) {
        // matches[0] -> str
        // matches[1] -> p1
        // matches[2] -> p2
        var key = matches[1];
        var value = matches[2];
        result.add(key, value);
    }

    return result;
};

demo.utils.getPageParametersAsQueryString = function(url) {
    var url = url || dojo.doc.URL;
    var parts = url.split("?");
    var requestPath = parts[0];
    var queryString = parts[1] ? "?" + parts[1] : "";

    return queryString;
};

/**
 * Build a link.
 * @param {Object} args
 *      args.id
 *      args.text
 *      args.style
 *      args.class
 *      args.url
 *      args.onclick
 *      args.help
 */
demo.utils.buildLink = function(args) {
    // inner function to format string pattern
    var _format = function(pattern, value) {
        var formatted = demo.utils.format(pattern, value);
        return formatted;
    };

    // build attributes
    var attributes = [];

    // id
    if (args.id) {
        attributes.push(_format('id="{0}"', args.id));
    }

    // class
    if (args["class"]) {
        attributes.push(_format('class="{0}"', args["class"]));
    }

    // style
    if (args.style) {
        attributes.push(_format('style="{0}"', args.style));
    }

    // url
    if (args.url) {
        attributes.push(_format('href="{0}"', args.url));
    } else {
        attributes.push(_format('href="{0}"', "javascript://"));
    }

    // onclick
    if (args.onclick) {
        attributes.push(_format('onclick="{0}"', args.onclick));
    }

    // target
    if (args.target) {
        attributes.push(_format('target="{0}"', args.target));
    }

    // help text
    if (args.help) {
        attributes.push(_format('title="{0}"', args.help));
    }

    // build link HTML
    var parts = [];
    parts.push("<a");
    parts.push(" ");
    parts.push(attributes.join(" "));
    parts.push(">");

    // If this is a popup link include the w3 popup image.
    if (args.type === "popup") {
        parts.push('<img src="../w3/images/icon-link-popup.gif" />');
        parts.push(" ");
    }

    // link text
    parts.push(args.text || "");

    parts.push("</a>");

    var link = parts.join("");
    return link;
};

/**
 * Show a DOM element.
 * @param {String} id The element ID.
 */
demo.utils.show = function(id) {
    var element = dojo.byId(id);

    if (!element) {
        return;
    }

    if (element.style.display === 'none') {
        element.style.display = '';
    }
};

/**
 * Hide a DOM element.
 * @param {String} id The element ID.
 */
demo.utils.hide = function(id) {
    var element = dojo.byId(id);

    if (!element) {
        return;
    }

    element.style.display = 'none';
};

/**
 * Check if a DOM element is visible.
 * @param {String} id The element ID.
 * @return {Boolean} true if the element is visible.
 */
demo.utils.isVisible = function(id) {
    var element = dojo.byId(id);

    if (!element) {
        return false;
    }

    return element.style.display === 'none' ? false : true;
};

/**
 * Enable a DOM element or a Dojo widget.
 * @param {String} id The element ID.
 */
demo.utils.enable = function(id) {
    var target = null;

    if (dijit.byId(id)) {
        target = dijit.byId(id);
        target.attr('disabled', false);
    } else if (dojo.byId(id)) {
        target = dojo.byId(id);
        dojo.attr(target, 'disabled', false);
        dojo.attr(target, 'aria-disabled', false);
    }
};

/**
 * Disable a DOM element or a Dojo widget.
 * @param {String} id The element ID.
 */
demo.utils.disable = function(id) {
    var target = null;

    if (dijit.byId(id)) {
        target = dijit.byId(id);
        target.attr('disabled', true);
    } else if (dojo.byId(id)) {
        target = dojo.byId(id);
        dojo.attr(target, 'disabled', true);
        dojo.attr(target, 'aria-disabled', true);
    }
};

/**
 * Get a instance which extends the parent object with overridden properties.
 *
 * @param {Object} parent - The parent object.
 * @param {Object?} props - The overridden properties.
 * @return {Object} The extended instance.
 */
demo.utils.extend = function(parent, props) {
    function F() {}
    F.prototype = parent;
    var instance = new F();

    dojo.mixin(instance, props);

    return instance;
};

demo.utils._getDebugMsg = function() {
    var args = [];
    for (var i = 0; i< arguments.length; i += 1) {
        args.push(arguments[i]);
    }
    var msg = args.join(" ");
    return msg;
};